﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using MSHTML;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Runtime.InteropServices;


namespace Givenchy
{
    class BrowserPipe
    {
        private int SLEEPTIME = 500;
        private int SCROLL = 560;
        private object Empty = 0;
        private int TIMEOUT = 1;

        public void process(List<BrowserAction> actions)
        {
            SHDocVw.InternetExplorer IE = new SHDocVw.InternetExplorer();

            string target = "";
            string name = "";
            bool testMode = false;
            int time_count = 0;

            int top = 0;
            int left = 0;
            int random = 0;

            log("action total : " + actions.Count, 2);
            foreach (BrowserAction n in actions)
            {
                //log(n.action + "/" + n.target + "/" + n.value + "/" + name + "/" + target);
                if (n.check())
                {
                    target = "";
                    name = "";
                    if (n.target.Contains("|"))
                    {
                        name = n.target.Substring(0, n.target.IndexOf("|"));
                        target = n.target.Substring(n.target.IndexOf("|") + 1);
                    }
                    try
                    {
                        log(n.action + " = " + n.target + " = " + n.value + " = " + name + " = " + target, 2);
                        switch (n.action)
                        {
                            case "alexa":// 清除alexa
                                Helper.GetRegValue("\\HKEY_CURRENT_USER\\Software\\Alexa Internet\\Alexa9\\ALX\\",
                                    "AID", null);
                                break;
                            case "open":// 打开页面

                                IE.Visible = true;
                                IE.Navigate2(n.target, ref Empty, ref Empty, ref Empty, ref Empty);

                                break;
                            case "injection":// 插入测试代码
                                testMode = true;
                                IE.Document.parentWindow.execScript("var s = document.createElement('script'); s.language = 'JavaScript';s.src='http://web.loozo.cn/js/injection.js?'+new Date().getTime();document.body.insertAdjacentElement('afterBegin',s);");

                                break;
                            case "sleep":// 在页面停留
                                int sleep = int.Parse(n.target) * (1000 / SLEEPTIME);
                                for (int sl = 0; sl <= sleep; sl++)
                                {
                                    System.Threading.Thread.Sleep(SLEEPTIME);
                                    Application.DoEvents();
                                }

                                break;
                            case "click":// 点击

                                random = 0;
                                if (n.value != "")
                                {
                                    try
                                    {
                                        random = AutoSeedRandoms(Int16.Parse(n.value));
                                    }
                                    catch (Exception e)
                                    {
                                        log(e.ToString(), 3);
                                    }
                                }

                                if (name == "id")
                                {
                                    IHTMLElement element = IE.Document.getElementById(target);
                                    if (element != null)
                                    {
                                        if (element.tagName.ToLower() == "a") ((IHTMLAnchorElement)element).target = "_self";
                                        IE.Document.getElementById(target).click();
                                    }
                                }
                                else if (name == "name")
                                {
                                    foreach (IHTMLElement element in IE.Document.getElementsByName(target))
                                    {
                                        element.click();
                                        break;
                                    }
                                }
                                else if (name == "link")
                                {
                                    int count = 0;
                                    foreach (IHTMLAnchorElement element in IE.Document.links)
                                    {
                                        //log("link random = " + (el.href.IndexOf(target) > -1) + "|" + (count == random), 3);
                                        if (element.href.IndexOf(target) > -1)
                                        {
                                            if (count > random)
                                            {
                                                element.target = "_self";
                                                ((IHTMLElement)element).click();
                                                break;
                                            }
                                            count++;
                                        }

                                    }
                                }
                                break;
                            case "iframead"://点击广告
                                top = 0;
                                left = 0;
                                if (n.value != "")
                                {
                                    try
                                    {
                                        left = Int16.Parse(n.value.Split('|')[0]);
                                        top = Int16.Parse(n.value.Split('|')[1]);
                                    }
                                    catch (Exception e)
                                    {
                                        log(e.ToString(), 3);
                                    }
                                }
                                if (name == "id")
                                {
                                    IHTMLWindow2 element = IE.Document.frames(target);
                                    if (element != null)
                                    {
                                        //IHTMLElement Element = ((IHTMLElement)iframes.item(0));
                                        updateIFrame((IHTMLWindow2)element);
                                        clickElement(IE.Document.getElementById(target), IE, top, left, testMode);
                                        clickElement(IE.Document.getElementById(target), IE, top, left, testMode);
                                    }

                                }
                                else if (name == "name")
                                {
                                    IHTMLElementCollection iframes = IE.Document.frames(target);
                                    if (iframes.length > 0)
                                    {
                                        IHTMLElement element = ((IHTMLElement)iframes.item(0));
                                        updateIFrame((IHTMLWindow2)element);
                                        clickElement(element, IE, top, left, testMode);
                                    }
                                }
                                else
                                {
                                    IHTMLElementCollection iframes = IE.Document.frames;

                                    if (iframes.length > 0)
                                    {
                                        IHTMLElement element = ((IHTMLElement)iframes.item(0));
                                        updateIFrame((IHTMLWindow2)element);
                                        clickElement(element, IE, top, left, testMode);
                                    }

                                }

                                //break;
                                //log(top +"" + left); 
                                break;
                            case "clickad":
                                top = 0;
                                left = 0;
                                random = 0;
                                if (n.value != "")
                                {
                                    try
                                    {
                                        left = Int16.Parse(n.value.Split('|')[0]);
                                        top = Int16.Parse(n.value.Split('|')[1]);
                                        random = Int16.Parse(n.value.Split('|')[2]);
                                    }
                                    catch (Exception e)
                                    {
                                        log(e.ToString(), 3);
                                    }
                                }


                                if (name == "id")
                                {
                                    IHTMLElement element = IE.Document.getElementById(target);
                                    if (element != null)
                                    {
                                        try
                                        {
                                            ((HTMLAnchorElement)element).target = "_self";
                                        }
                                        catch (Exception e)
                                        {
                                            log(e.ToString(), 3);
                                        }
                                        clickElement(element, IE, top, left, testMode);
                                    }
                                }
                                else if (name == "name")
                                {
                                    IHTMLElement element = IE.Document.getElementByName(target);
                                    if (element != null)
                                    {
                                        try
                                        {
                                            ((HTMLAnchorElement)element).target = "_self";
                                        }
                                        catch (Exception e)
                                        {
                                            log(e.ToString(), 3);
                                        }
                                        clickElement(element, IE, top, left, testMode);
                                    }
                                }
                                else if (name == "link")
                                {
                                    int count = 0;
                                    foreach (HTMLAnchorElement element in IE.Document.links)
                                    {
                                        if (element.href.IndexOf(target) > -1)
                                        {
                                            if (count == random)
                                            {
                                                try
                                                {
                                                    element.target = "_self";
                                                }
                                                catch (Exception e)
                                                {
                                                    log(e.ToString(), 3);
                                                }
                                                clickElement((IHTMLElement)element, IE, top, left, testMode);
                                                break;
                                            }
                                            count++;
                                        }
                                    }
                                }
                                else if (name == "linkregex")
                                {
                                    int count = 0;
                                    foreach (HTMLAnchorElement element in IE.Document.links)
                                    {
                                        if (Regex.IsMatch(element.href, target))
                                        {
                                            if (count == random)
                                            {
                                                try
                                                {
                                                    element.target = "_self";
                                                }
                                                catch (Exception e)
                                                {
                                                    log(e.ToString(), 3);
                                                }
                                                clickElement((IHTMLElement)element, IE, top, left, testMode);
                                                break;
                                            }
                                            count++;
                                        }
                                    }
                                }
                                else if (name == "titleregex")
                                {
                                    int count = 0;
                                    foreach (HTMLAnchorElement element in IE.Document.links)
                                    {
                                        if (element.innerText != null)
                                        {
                                            log(element.innerText + "|" + target + "|" + Regex.IsMatch(element.innerText, target), 3);
                                            if (Regex.IsMatch(element.innerText, target))
                                            {
                                                if (count == random)
                                                {
                                                    try
                                                    {
                                                        element.target = "_self";
                                                    }
                                                    catch (Exception e)
                                                    {
                                                        log(e.ToString(), 3);
                                                    }
                                                    clickElement((IHTMLElement)element, IE, top, left, testMode);
                                                    //log("OK" + target + "|" + el.innerText);
                                                    break;
                                                }
                                                count++;
                                            }
                                        }

                                    }
                                }
                                break;
                            case "input":

                                if (name == "id")
                                {
                                    if (IE.Document.getElementById(target) != null)
                                    {
                                        IE.Document.getElementById(target).value = n.value;
                                    }
                                }
                                break;
                            case "quit":
                                IE.Quit();
                                break;
                            case "key":
                                clickWindow((IntPtr)IE.HWND, 0, 0, int.Parse(n.target), false);
                                break;
                        }
                    }
                    catch (Exception e)
                    {
                        log(e.ToString(), 3);
                        break;

                    }
                    try
                    {
                        time_count = 0;
                        while (IE.Busy || IE.ReadyState != SHDocVw.tagREADYSTATE.READYSTATE_COMPLETE)
                        {
                            if (time_count > (TIMEOUT * 60 * 1000))
                            {
                                IE.Stop();
                                break;
                            }
                            time_count = time_count + SLEEPTIME;
                            log("Wait IE Busy..." + time_count, 3);
                            System.Threading.Thread.Sleep(SLEEPTIME);
                            Application.DoEvents();

                        }
                    }
                    catch (Exception e)
                    {
                        log(e.ToString(), 3);
                        break;
                    }
                    //统一最大化
                    PostMessage((IntPtr)IE.HWND, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
                }
            }
        }

        private void updateIFrame(IHTMLWindow2 inner)
        {
            IHTMLDocument2 doc = (IHTMLDocument2)GetDocumentFromWindow(inner);


            foreach (IHTMLAnchorElement el in doc.links)
            {
                try
                {
                    el.target = "_top";
                }
                catch (Exception e)
                {
                    log(e.ToString(), 3);
                }
            }
        }
        /// <summary>
        /// 根据元素，计算位置，并点击他
        /// </summary>
        /// <param name="Element"></param>
        /// <param name="IEHWND"></param>
        /// <param name="top"></param>
        /// <param name="left"></param>
        /// <param name="testMode"></param>
        private void clickElement(IHTMLElement Element, SHDocVw.InternetExplorer IEHWND, int top, int left, bool testMode)
        {
            //PostMessage((IntPtr)IEHWND.HWND, WM_SYSCOMMAND, SC_MAXIMIZE, 0);
            int ElementLeft = Element.offsetLeft;
            int ElementTop = Element.offsetTop;
            IHTMLElement TmpElem = Element.offsetParent;
            while (TmpElem != null)
            {
                ElementLeft = ElementLeft + TmpElem.offsetLeft;
                ElementTop = ElementTop + TmpElem.offsetTop;
                TmpElem = TmpElem.offsetParent;
            }


            ElementTop = ElementTop + top;
            ElementLeft = ElementLeft + left;
            if (ElementTop > SCROLL)
            {

                IEHWND.Document.parentWindow.execScript("window.scrollTo(0," + (ElementTop - SCROLL) + ");");
                ElementTop = SCROLL;
            }
            clickWindow((IntPtr)IEHWND.HWND, ElementLeft, ElementTop, MOUSE_MOVE, testMode);
            System.Threading.Thread.Sleep(500);
            clickWindow((IntPtr)IEHWND.HWND, ElementLeft, ElementTop, MOUSE_MOVE, testMode);
            System.Threading.Thread.Sleep(800);
            clickWindow((IntPtr)IEHWND.HWND, ElementLeft, ElementTop, LEFT_CLICK, testMode);
            //System.Threading.Thread.Sleep(300);
            // clickWindow((IntPtr)IEHWND.HWND, ElementLeft, ElementTop, LEFT_CLICK, testMode);
            TmpElem = null;
        }
        private void clickElement(IHTMLElement Element, SHDocVw.InternetExplorer IEHWND)
        {
            clickElement(Element, IEHWND, 0, 0, false);
        }
        // 生成随机数
        private int AutoSeedRandoms(int send)
        {
            // Wait to allow the timer to advance.
            System.Threading.Thread.Sleep(1);
            Random autoRand = new Random(Guid.NewGuid().GetHashCode());
            return autoRand.Next(send);
        }


        int LEFT_CLICK = 1;
        int RIGHT_CLICK = 2;
        int MOUSE_MOVE = 3;
        int KEY_ENTER = 0x0D;
        // PostMessage
        uint WM_LBUTTONDOWN = 0x201;
        uint WM_LBUTTONUP = 0x202;
        uint WM_RBUTTONDOWN = 0x0204;
        uint WM_RBUTTONUP = 0x0205;
        uint WM_MOUSEMOVE = 0x0200;

        uint WM_KEYDOWN = 0x0100;

        uint WM_KEYUP = 0x0101;
        /// <summary>
        /// 向IE文档的发送postmessage（点击，移动等）
        /// </summary>
        /// <param name="winHwnd"></param>
        /// <param name="left"></param>
        /// <param name="top"></param>
        /// <param name="action"></param>
        /// <param name="testMode"></param>
        private void clickWindow(IntPtr winHwnd, int left, int top, int action, bool testMode)
        {
            //IntPtr winHwnd = FindWindow("IEFrame", null); 
            if (winHwnd != IntPtr.Zero)
            {
                IntPtr child = IntPtr.Zero;
                if (Helper.getIEVersion() == 6)
                {
                    //IntPtr child = FindWindowEx(winHwnd, 0, "Frame Tab", null);
                    //child = FindWindowEx(child, 0, "TabWindowClass", null);
                    child = FindWindowEx(winHwnd, 0, "Shell DocObject View", null);
                    child = FindWindowEx(child, 0, "Internet Explorer_Server", null);
                }
                else
                {
                    child = FindWindowEx(winHwnd, 0, "Frame Tab", null);
                    child = FindWindowEx(child, 0, "TabWindowClass", null);
                    child = FindWindowEx(child, 0, "Shell DocObject View", null);
                    child = FindWindowEx(child, 0, "Internet Explorer_Server", null);
                }


                if (child != IntPtr.Zero)
                {
                    //RECT rect = new RECT();
                    //GetWindowRect(child, ref rect);

                    //log(rect.Left + "|" + rect.Top);
                    //todo 随机偏移量应该可以用连接或者目标elements的大小作为种子，
                    // 这样就根本不需要手动设置偏移量了。
                    // 应该吧clickElement和ClickWindow分成计算坐标和点击两种方法，把功能划分细致。
                    log("Default : " + left + "|" + top, 2);
                    if (!testMode)
                    {
                        left = AutoSeedRandoms(30) + left + 1;
                        top = AutoSeedRandoms(10) + top + 1;
                        log("This NotTest Offset left=" + left + " top=" + top, 2);
                    }

                    //log(left + "|" + top);
                    switch (action)
                    {
                        case 3://MOUSE_MOVE
                            PostMessage(child, WM_MOUSEMOVE, 0, left + (top << 16));
                            break;
                        case 2://RIGHT_CLICK
                            PostMessage(child, WM_RBUTTONDOWN, 0, left + (top << 16));
                            System.Threading.Thread.Sleep(200);
                            PostMessage(child, WM_RBUTTONUP, 0, left + (top << 16));
                            break;
                        case 1: //LEFT_CLICK
                            PostMessage(child, WM_LBUTTONDOWN, 0, left + (top << 16));
                            System.Threading.Thread.Sleep(200);
                            PostMessage(child, WM_LBUTTONUP, 0, left + (top << 16));
                            break;
                        case 0x0D:
                            PostMessage(child, WM_KEYDOWN, KEY_ENTER, 0);
                            System.Threading.Thread.Sleep(200);
                            PostMessage(child, WM_KEYUP, KEY_ENTER, 0);
                            log("Enter OK", 2);
                            break;
                        //wm_keydown,   vk_return

                    }
                    //Application.DoEvents();
                    //System.Threading.Thread.Sleep(1000);

                }

                child = IntPtr.Zero;
            }
        }

        //  获取 iFrames 内的Doc
        public IHTMLDocument3 GetDocumentFromWindow(IHTMLWindow2 htmlWindow)
        {
            if (htmlWindow == null)
            {
                return null;
            }

            // First try the usual way to get the document.
            try
            {
                IHTMLDocument3 doc = (IHTMLDocument3)htmlWindow.document;
                return doc;
            }
            catch (COMException comEx)
            {
                // I think COMException won't be ever fired but just to be sure ...
                if (comEx.ErrorCode != E_ACCESSDENIED)
                {
                    return null;
                }
            }
            catch (System.UnauthorizedAccessException)
            {
            }
            catch
            {
                // Any other error.
                return null;
            }

            // At this point the error was E_ACCESSDENIED because the frame contains a document from another domain.
            // IE tries to prevent a cross frame scripting security issue.
            try
            {
                // Convert IHTMLWindow2 to IWebBrowser2 using IServiceProvider.
                IServiceProvider sp = (IServiceProvider)htmlWindow;

                // Use IServiceProvider.QueryService to get IWebBrowser2 object.
                Object brws = null;
                sp.QueryService(ref IID_IWebBrowserApp, ref IID_IWebBrowser2, out brws);

                // Get the document from IWebBrowser2.
                SHDocVw.IWebBrowser2 browser = (SHDocVw.IWebBrowser2)(brws);

                return (IHTMLDocument3)browser.Document;
            }
            catch
            {
            }

            return null;
        }


        //获取窗口大小及位置:需要调用方法GetWindowRect(IntPtr hWnd, ref RECT lpRect)
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);

        [StructLayout(LayoutKind.Sequential)]
        public struct RECT
        {
            public int Left;                             //最左坐标
            public int Top;                             //最上坐标
            public int Right;                           //最右坐标
            public int Bottom;                        //最下坐标
        }

        public const int WM_SYSCOMMAND = 0x112;
        public const int SC_MINIMIZE = 0xF020;
        public const int SC_MAXIMIZE = 0xF030;

        //
        [DllImport("user32.dll", SetLastError = true)]
        static extern bool PostMessage(IntPtr hWnd, uint Msg, int wParam, int lParam);

        [DllImport("user32.dll", EntryPoint = "FindWindow")]
        private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        static extern IntPtr FindWindowEx(IntPtr hwndParent, int hwndChildAfter, string lpszClass, string lpszWindow);


        private const int E_ACCESSDENIED = unchecked((int)0x80070005L);
        private Guid IID_IWebBrowserApp = new Guid("0002DF05-0000-0000-C000-000000000046");
        private Guid IID_IWebBrowser2 = new Guid("D30C1661-CDAF-11D0-8A3E-00C04FC9E26E");
        private Main main;

        public BrowserPipe(Main main)
        {

            this.main = main;
        }

        private void log(string t, int level)
        {
            this.main.log(t, level);
        }


        // This is the COM IServiceProvider interface, not System.IServiceProvider .Net interface!
        [ComImport(), ComVisible(true), Guid("6D5140C1-7436-11CE-8034-00AA006009FA"),
        InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IServiceProvider
        {
            [return: MarshalAs(UnmanagedType.I4)]
            [PreserveSig]
            int QueryService(ref Guid guidService, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out object ppvObject);
        }
    }

    class EventHandlers
    {
        public void OnBeforeNavigate2(object sender, ref object URL,
                                      ref object Flags, ref object Target,
                                      ref object PostData, ref object Headers,
                                      ref bool Cancel)
        {
            //Console.WriteLine("BeforeNavigate2 fired!");
        }
    }

    public class BrowserAction
    {
        public string action = "";
        public string target = "";
        public string value = "";
        public BrowserAction(string a, string t)
        {
            action = a;
            target = t;
        }
        public BrowserAction(string a, string t, string v)
        {
            action = a;
            target = t;
            value = v;
        }
        public bool check()
        {
            if (action.Equals("") || target.Equals(""))
            {
                return false;
            }
            return true;
        }
    }

}
